package com.dexafree.materiallist.view;

import com.dexafree.materiallist.card.Card;
import com.dexafree.materiallist.card.CardLayout;
import com.dexafree.materiallist.card.event.DismissEvent;
import ohos.agp.components.*;
import ohos.agp.render.render3d.ViewHolder;
import utils.LogUtil;

import java.util.*;

public class MaterialListAdapter extends BaseItemProvider implements Observer {
    private final MaterialListView.OnSwipeAnimation mSwipeAnimation;
    private final MaterialListView.OnAdapterItemsChanged mItemAnimation;
    private final List<Card> mCardList = new ArrayList<>();

    public MaterialListAdapter(final MaterialListView.OnSwipeAnimation swipeAnimation,
                               final MaterialListView.OnAdapterItemsChanged itemAnimation) {
        mSwipeAnimation = swipeAnimation;
        mItemAnimation = itemAnimation;
    }

    @Override
    public int getCount() {
        return mCardList.size();
    }

    @Override
    public Object getItem(int i) {
        if (i >= 0 && i < mCardList.size()) {
            return mCardList.get(i);
        }
        return null;
    }



    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
        final Component cpt;
        ViewHolder holder;
        if (component == null) {
            cpt = LayoutScatter.getInstance(componentContainer.getContext()).parse(getItemComponentType(i), componentContainer, false);
            holder = new ViewHolder(cpt);
            cpt.setTag(holder);
        } else {
            cpt = component;
            // 从缓存中获取到列表项实例后，直接使用绑定的子组件信息进行数据填充。
            holder = (ViewHolder) cpt.getTag();
        }
        holder.build(getCard(i));
        return cpt;
    }

    public class ViewHolder {
        private final CardLayout view;

        public ViewHolder(final Component v) {
            view = (CardLayout) v;
        }

        public void build(Card card) {
            view.build(card);
        }
    }

    @Override
    public int getItemComponentType(int position) {
        return mCardList.get(position).getProvider().getLayout();
    }

//    @Override
//    public ViewHolder onCreateViewHolder(final ComponentContainer parent, final int viewType) {
//        return new ViewHolder(LayoutScatter.getInstance(parent.getContext())
//                .parse(viewType, parent, false));
//    }
//
//    @Override
//    public void onBindViewHolder(final ViewHolder holder, final int position) {
//        holder.build(getCard(position));
//    }
//
//    @Override
//    public int getItemCount() {
//        return mCardList.size();
//    }


    /**
     * Add a Card at a specific position with or without a scroll animation.
     *
     * @param position of the card to insert.
     * @param card     to insert.
     * @param scroll   will trigger an animation if it is set to <code>true</code> otherwise not.
     */
    public void add(final int position, final Card card, final boolean scroll) {
        mCardList.add(position, card);
        card.getProvider().addObserver(this);
        mItemAnimation.onAddItem(position, scroll);
        notifyDataSetItemInserted(position); // Triggers the animation!
    }

    /**
     * Add a Card at a specific position.
     *
     * @param position of the card to insert.
     * @param card     to insert.
     */
    public void add(final int position, final Card card) {
        add(position, card, true);
    }

    /**
     * Add a Card at the start.
     *
     * @param card to add at the start.
     */
    public void addAtStart(final Card card) {
        add(0, card);
    }

    /**
     * Add a Card.
     *
     * @param card to add.
     */
    public void add(final Card card) {
        add(mCardList.size(), card);
    }

    /**
     * Add all Cards.
     *
     * @param cards to add.
     */
    public void addAll(final Card... cards) {
        addAll(Arrays.asList(cards));
    }

    /**
     * Add all Cards.
     *
     * @param cards to add.
     */
    public void addAll(final Collection<Card> cards) {
        int index = 0;
        for (Card card : cards) {
            add(index++, card, false);
        }
    }

    /**
     * Remove a Card withProvider or without an animation.
     *
     * @param card    to remove.
     * @param animate {@code true} to animate the remove process or {@code false} otherwise.
     */
    public void remove(final Card card, boolean animate) {
        if (card.isDismissible()) {
            card.getProvider().deleteObserver(this);
            if (animate) {
                mSwipeAnimation.animate(getPosition(card));
            } else {
                int delPos = mCardList.indexOf(card);
                mCardList.remove(card);
                mItemAnimation.onRemoveItem();
//                notifyDataChanged();
                notifyDataSetItemRemoved(delPos);
            }
        }
    }

    /**
     * Clears the list from all Cards (even if they are not dismissable).
     */
    public void clearAll() {
        while (!mCardList.isEmpty()) {
            final Card card = mCardList.get(0);
            card.setDismissible(true);
            remove(card, false);
            notifyDataSetItemRemoved(0);
        }
    }

    /**
     * Clears the list from all Cards (only if dismissable).
     */
    public void clear() {
        for (int index = 0; index < mCardList.size(); ) {
            final Card card = mCardList.get(index);
            if (!card.isDismissible()) {
                index++;
            }
            remove(card, false);
            notifyDataSetItemRemoved(index);
        }
    }

    /**
     * Is the list empty?
     *
     * @return {@code true} if the list is empty or {@code false} otherwise.
     */
    public boolean isEmpty() {
        return mCardList.isEmpty();
    }

    /**
     * Get a Card at the specified position.
     *
     * @param position of the Card.
     * @return the Card or {@code null} if the position is outside of the list range.
     */
    public Card getCard(int position) {
        if (position >= 0 && position < mCardList.size()) {
            return mCardList.get(position);
        }
        return null;
    }

    /**
     * Get the position of a specified Card.
     *
     * @param card to get the position of.
     * @return the position.
     */
    public int getPosition(Card card) {
        return mCardList.indexOf(card);
    }

    @Override
    public void update(final Observable observable, final Object data) {
        if (data instanceof DismissEvent) {
            remove(((DismissEvent) data).getCard(), true);
        }
        if (data instanceof Card) {
            notifyDataChanged();
        }
    }
}
